Skip to content

Commit 1b8141b

Browse files
authored
Update python orchestrations docs (#790)
* Update python orchestrations docs * Update author
1 parent 75f4ef0 commit 1b8141b

4 files changed

Lines changed: 615 additions & 254 deletions

File tree

agent-framework/migration-guide/from-autogen/index.md

Lines changed: 105 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
22
title: AutoGen to Microsoft Agent Framework Migration Guide
33
description: A comprehensive guide for migrating from AutoGen to the Microsoft Agent Framework Python SDK.
4-
author: ekzhu
4+
author: moonbox3
55
ms.topic: reference
6-
ms.author: ekzhu
6+
ms.author: evmattso
77
ms.date: 09/29/2025
88
ms.service: agent-framework
99
---
@@ -1195,85 +1195,134 @@ result = await team.run("Complex research and analysis task")
11951195
**Agent Framework Implementation:**
11961196

11971197
```python
1198+
from typing import cast
11981199
from agent_framework import (
1199-
MagenticBuilder, MagenticCallbackMode, WorkflowOutputEvent,
1200-
MagenticCallbackEvent, MagenticOrchestratorMessageEvent, MagenticAgentDeltaEvent
1200+
MAGENTIC_EVENT_TYPE_AGENT_DELTA,
1201+
MAGENTIC_EVENT_TYPE_ORCHESTRATOR,
1202+
AgentRunUpdateEvent,
1203+
ChatAgent,
1204+
ChatMessage,
1205+
MagenticBuilder,
1206+
WorkflowOutputEvent,
12011207
)
1208+
from agent_framework.openai import OpenAIChatClient
12021209

1203-
# Assume we have researcher, coder, and coordinator_client from previous examples
1204-
async def on_event(event: MagenticCallbackEvent) -> None:
1205-
if isinstance(event, MagenticOrchestratorMessageEvent):
1206-
print(f"[ORCHESTRATOR]: {event.message.text}")
1207-
elif isinstance(event, MagenticAgentDeltaEvent):
1208-
print(f"[{event.agent_id}]: {event.text}", end="")
1209-
1210-
workflow = (MagenticBuilder()
1211-
.participants(researcher=researcher, coder=coder)
1212-
.on_event(on_event, mode=MagenticCallbackMode.STREAMING)
1213-
.with_standard_manager(
1214-
chat_client=coordinator_client,
1215-
max_round_count=20,
1216-
max_stall_count=3,
1217-
max_reset_count=2
1218-
)
1219-
.build())
1210+
# Create a manager agent for orchestration
1211+
manager_agent = ChatAgent(
1212+
name="MagenticManager",
1213+
description="Orchestrator that coordinates the workflow",
1214+
instructions="You coordinate a team to complete complex tasks efficiently.",
1215+
chat_client=OpenAIChatClient(),
1216+
)
1217+
1218+
workflow = (
1219+
MagenticBuilder()
1220+
.participants(researcher=researcher, coder=coder)
1221+
.with_standard_manager(
1222+
agent=manager_agent,
1223+
max_round_count=20,
1224+
max_stall_count=3,
1225+
max_reset_count=2,
1226+
)
1227+
.build()
1228+
)
12201229

12211230
# Example usage (would be in async context)
12221231
async def magentic_example():
1232+
output: str | None = None
12231233
async for event in workflow.run_stream("Complex research task"):
1224-
if isinstance(event, WorkflowOutputEvent):
1225-
final_result = event.data
1234+
if isinstance(event, AgentRunUpdateEvent):
1235+
props = event.data.additional_properties if event.data else None
1236+
event_type = props.get("magentic_event_type") if props else None
1237+
1238+
if event_type == MAGENTIC_EVENT_TYPE_ORCHESTRATOR:
1239+
text = event.data.text if event.data else ""
1240+
print(f"[ORCHESTRATOR]: {text}")
1241+
elif event_type == MAGENTIC_EVENT_TYPE_AGENT_DELTA:
1242+
agent_id = props.get("agent_id", event.executor_id) if props else event.executor_id
1243+
if event.data and event.data.text:
1244+
print(f"[{agent_id}]: {event.data.text}", end="")
1245+
1246+
elif isinstance(event, WorkflowOutputEvent):
1247+
output_messages = cast(list[ChatMessage], event.data)
1248+
if output_messages:
1249+
output = output_messages[-1].text
12261250
```
12271251

12281252
**Agent Framework Customization Options:**
12291253

12301254
The Magentic workflow provides extensive customization options:
12311255

1232-
- **Manager configuration**: Custom orchestrator models and prompts
1256+
- **Manager configuration**: Use a ChatAgent with custom instructions and model settings
12331257
- **Round limits**: `max_round_count`, `max_stall_count`, `max_reset_count`
1234-
- **Event callbacks**: Real-time streaming with granular event filtering
1258+
- **Event streaming**: Use `AgentRunUpdateEvent` with `magentic_event_type` metadata
12351259
- **Agent specialization**: Custom instructions and tools per agent
1236-
- **Callback modes**: `STREAMING` for real-time updates or `BATCH` for final results
1237-
- **Human-in-the-loop planning**: Custom planner functions for interactive workflows
1260+
- **Human-in-the-loop**: Plan review, tool approval, and stall intervention
12381261

12391262
```python
12401263
# Advanced customization example with human-in-the-loop
1264+
from typing import cast
1265+
from agent_framework import (
1266+
MAGENTIC_EVENT_TYPE_AGENT_DELTA,
1267+
MAGENTIC_EVENT_TYPE_ORCHESTRATOR,
1268+
AgentRunUpdateEvent,
1269+
ChatAgent,
1270+
MagenticBuilder,
1271+
MagenticHumanInterventionDecision,
1272+
MagenticHumanInterventionKind,
1273+
MagenticHumanInterventionReply,
1274+
MagenticHumanInterventionRequest,
1275+
RequestInfoEvent,
1276+
WorkflowOutputEvent,
1277+
)
12411278
from agent_framework.openai import OpenAIChatClient
1242-
from agent_framework import MagenticBuilder, MagenticCallbackMode, MagenticPlannerContext
1243-
1244-
# Assume we have researcher_agent, coder_agent, analyst_agent, detailed_event_handler
1245-
# and get_human_input function defined elsewhere
1246-
1247-
async def custom_planner(context: MagenticPlannerContext) -> str:
1248-
"""Custom planner with human input for critical decisions."""
1249-
if context.round_count > 5:
1250-
# Request human input for complex decisions
1251-
return await get_human_input(f"Next action for: {context.current_state}")
1252-
return "Continue with automated planning"
1253-
1254-
workflow = (MagenticBuilder()
1255-
.participants(
1256-
researcher=researcher_agent,
1257-
coder=coder_agent,
1258-
analyst=analyst_agent
1259-
)
1260-
.with_standard_manager(
1261-
chat_client=OpenAIChatClient(model_id="gpt-5"),
1262-
max_round_count=15, # Limit total rounds
1263-
max_stall_count=2, # Prevent infinite loops
1264-
max_reset_count=1, # Allow one reset on failure
1265-
orchestrator_prompt="Custom orchestration instructions..."
1266-
)
1267-
.with_planner(custom_planner) # Human-in-the-loop planning
1268-
.on_event(detailed_event_handler, mode=MagenticCallbackMode.STREAMING)
1269-
.build())
1279+
1280+
# Create manager agent with custom configuration
1281+
manager_agent = ChatAgent(
1282+
name="MagenticManager",
1283+
description="Orchestrator for complex tasks",
1284+
instructions="Custom orchestration instructions...",
1285+
chat_client=OpenAIChatClient(model_id="gpt-4o"),
1286+
)
1287+
1288+
workflow = (
1289+
MagenticBuilder()
1290+
.participants(
1291+
researcher=researcher_agent,
1292+
coder=coder_agent,
1293+
analyst=analyst_agent,
1294+
)
1295+
.with_standard_manager(
1296+
agent=manager_agent,
1297+
max_round_count=15, # Limit total rounds
1298+
max_stall_count=2, # Trigger stall handling
1299+
max_reset_count=1, # Allow one reset on failure
1300+
)
1301+
.with_plan_review() # Enable human plan review
1302+
.with_human_input_on_stall() # Enable human intervention on stalls
1303+
.build()
1304+
)
1305+
1306+
# Handle human intervention requests during execution
1307+
async for event in workflow.run_stream("Complex task"):
1308+
if isinstance(event, RequestInfoEvent) and event.request_type is MagenticHumanInterventionRequest:
1309+
req = cast(MagenticHumanInterventionRequest, event.data)
1310+
if req.kind == MagenticHumanInterventionKind.PLAN_REVIEW:
1311+
# Review and approve the plan
1312+
reply = MagenticHumanInterventionReply(
1313+
decision=MagenticHumanInterventionDecision.APPROVE
1314+
)
1315+
async for ev in workflow.send_responses_streaming({event.request_id: reply}):
1316+
pass # Handle continuation
12701317
```
12711318

12721319
For detailed Magentic examples, see:
12731320

12741321
- [Basic Magentic Workflow](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/workflows/orchestration/magentic.py) - Standard orchestrated multi-agent workflow
12751322
- [Magentic with Checkpointing](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/workflows/orchestration/magentic_checkpoint.py) - Persistent orchestrated workflows
1276-
- [Magentic Human Plan Update](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/workflows/orchestration/magentic_human_plan_update.py) - Human-in-the-loop planning
1323+
- [Magentic Human Plan Update](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/workflows/orchestration/magentic_human_plan_update.py) - Human-in-the-loop plan review
1324+
- [Magentic Agent Clarification](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/workflows/orchestration/magentic_agent_clarification.py) - Tool approval for agent clarification
1325+
- [Magentic Human Replan](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/workflows/orchestration/magentic_human_replan.py) - Human intervention on stalls
12771326

12781327
#### Future Patterns
12791328

agent-framework/user-guide/workflows/orchestrations/group-chat.md

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -213,25 +213,41 @@ def select_next_speaker(state: GroupChatStateSnapshot) -> str | None:
213213
# Build the group chat workflow
214214
workflow = (
215215
GroupChatBuilder()
216-
.select_speakers(select_next_speaker, display_name="Orchestrator")
216+
.set_select_speakers_func(select_next_speaker, display_name="Orchestrator")
217217
.participants([researcher, writer])
218218
.build()
219219
)
220220
```
221221

222-
## Configure Group Chat with Prompt-Based Manager
222+
## Configure Group Chat with Agent-Based Manager
223223

224-
Alternatively, use an AI-powered manager for dynamic speaker selection:
224+
Alternatively, use an agent-based manager for intelligent speaker selection. The manager is a full `ChatAgent` with access to tools, context, and observability:
225225

226226
```python
227-
# Build group chat with prompt-based manager
227+
# Create coordinator agent for speaker selection
228+
coordinator = ChatAgent(
229+
name="Coordinator",
230+
description="Coordinates multi-agent collaboration by selecting speakers",
231+
instructions="""
232+
You coordinate a team conversation to solve the user's task.
233+
234+
Review the conversation history and select the next participant to speak.
235+
236+
Guidelines:
237+
- Start with Researcher to gather information
238+
- Then have Writer synthesize the final answer
239+
- Only finish after both have contributed meaningfully
240+
- Allow for multiple rounds of information gathering if needed
241+
""",
242+
chat_client=chat_client,
243+
)
244+
245+
# Build group chat with agent-based manager
228246
workflow = (
229247
GroupChatBuilder()
230-
.set_prompt_based_manager(
231-
chat_client=chat_client,
232-
display_name="Coordinator"
233-
)
234-
.participants(researcher=researcher, writer=writer)
248+
.set_manager(coordinator, display_name="Orchestrator")
249+
.with_termination_condition(lambda messages: sum(1 for msg in messages if msg.role == Role.ASSISTANT) >= 4)
250+
.participants([researcher, writer])
235251
.build()
236252
)
237253
```
@@ -241,24 +257,39 @@ workflow = (
241257
Execute the workflow and process events:
242258

243259
```python
244-
from agent_framework import AgentRunUpdateEvent, WorkflowOutputEvent
260+
from typing import cast
261+
from agent_framework import AgentRunUpdateEvent, Role, WorkflowOutputEvent
245262

246263
task = "What are the key benefits of async/await in Python?"
247264

248265
print(f"Task: {task}\n")
249266
print("=" * 80)
250267

268+
final_conversation: list[ChatMessage] = []
269+
last_executor_id: str | None = None
270+
251271
# Run the workflow
252272
async for event in workflow.run_stream(task):
253273
if isinstance(event, AgentRunUpdateEvent):
254274
# Print streaming agent updates
255-
print(f"[{event.executor_id}]: {event.data}", end="", flush=True)
275+
eid = event.executor_id
276+
if eid != last_executor_id:
277+
if last_executor_id is not None:
278+
print()
279+
print(f"[{eid}]:", end=" ", flush=True)
280+
last_executor_id = eid
281+
print(event.data, end="", flush=True)
256282
elif isinstance(event, WorkflowOutputEvent):
257-
# Workflow completed
258-
final_message = event.data
259-
author = getattr(final_message, "author_name", "System")
260-
text = getattr(final_message, "text", str(final_message))
261-
print(f"\n\n[{author}]\n{text}")
283+
# Workflow completed - data is a list of ChatMessage
284+
final_conversation = cast(list[ChatMessage], event.data)
285+
286+
if final_conversation:
287+
print("\n\n" + "=" * 80)
288+
print("Final Conversation:")
289+
for msg in final_conversation:
290+
author = getattr(msg, "author_name", "Unknown")
291+
text = getattr(msg, "text", str(msg))
292+
print(f"\n[{author}]\n{text}")
262293
print("-" * 80)
263294

264295
print("\nWorkflow completed.")
@@ -314,13 +345,14 @@ Workflow completed.
314345

315346
::: zone pivot="programming-language-python"
316347

317-
- **Flexible Manager Strategies**: Choose between simple selectors, prompt-based managers, or custom logic
348+
- **Flexible Manager Strategies**: Choose between simple selectors, agent-based managers, or custom logic
318349
- **GroupChatBuilder**: Creates workflows with configurable speaker selection
319-
- **select_speakers()**: Define custom Python functions for speaker selection
320-
- **set_prompt_based_manager()**: Use AI-powered coordination for dynamic speaker selection
350+
- **set_select_speakers_func()**: Define custom Python functions for speaker selection
351+
- **set_manager()**: Use an agent-based manager for intelligent speaker coordination
321352
- **GroupChatStateSnapshot**: Provides conversation state for selection decisions
322353
- **Iterative Collaboration**: Agents build upon each other's contributions
323-
- **Event Streaming**: Process agent updates and outputs in real-time
354+
- **Event Streaming**: Process `AgentRunUpdateEvent` and `WorkflowOutputEvent` in real-time
355+
- **list[ChatMessage] Output**: All orchestrations return a list of chat messages
324356

325357
::: zone-end
326358

@@ -403,7 +435,7 @@ def smart_selector(state: GroupChatStateSnapshot) -> str | None:
403435

404436
workflow = (
405437
GroupChatBuilder()
406-
.select_speakers(smart_selector, display_name="SmartOrchestrator")
438+
.set_select_speakers_func(smart_selector, display_name="SmartOrchestrator")
407439
.participants([researcher, writer])
408440
.build()
409441
)

0 commit comments

Comments
 (0)