|
1 | 1 | """ |
2 | 2 | Agent Framework MagenticOne Example - Travel Planning with Multiple Agents |
| 3 | +pip install agent-framework-orchestrations==1.0.0b260212 |
3 | 4 | """ |
4 | 5 | import asyncio |
| 6 | +import json |
5 | 7 | import os |
6 | 8 | from typing import cast |
7 | 9 |
|
8 | | -from agent_framework import ( |
9 | | - AgentRunUpdateEvent, |
10 | | - ChatAgent, |
11 | | - ChatMessage, |
12 | | - MagenticBuilder, |
13 | | - MagenticOrchestratorEvent, |
14 | | - MagenticProgressLedger, |
15 | | - WorkflowOutputEvent, |
16 | | -) |
| 10 | +from agent_framework import Agent, AgentResponseUpdate, Message, WorkflowEvent |
17 | 11 | from agent_framework.openai import OpenAIChatClient |
| 12 | +from agent_framework.orchestrations import MagenticBuilder, MagenticProgressLedger |
18 | 13 | from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider |
19 | 14 | from dotenv import load_dotenv |
20 | 15 | from rich.console import Console |
|
54 | 49 | console = Console() |
55 | 50 |
|
56 | 51 | # Create the agents |
57 | | -local_agent = ChatAgent( |
58 | | - chat_client=client, |
| 52 | +local_agent = Agent( |
| 53 | + client=client, |
59 | 54 | instructions=( |
60 | 55 | "You are a helpful assistant that can suggest authentic and interesting local activities " |
61 | 56 | "or places to visit for a user and can utilize any context information provided." |
|
64 | 59 | description="A local assistant that can suggest local activities or places to visit.", |
65 | 60 | ) |
66 | 61 |
|
67 | | -language_agent = ChatAgent( |
68 | | - chat_client=client, |
| 62 | +language_agent = Agent( |
| 63 | + client=client, |
69 | 64 | instructions=( |
70 | 65 | "You are a helpful assistant that can review travel plans, providing feedback on important/critical " |
71 | 66 | "tips about how best to address language or communication challenges for the given destination. " |
|
75 | 70 | description="A helpful assistant that can provide language tips for a given destination.", |
76 | 71 | ) |
77 | 72 |
|
78 | | -travel_summary_agent = ChatAgent( |
79 | | - chat_client=client, |
| 73 | +travel_summary_agent = Agent( |
| 74 | + client=client, |
80 | 75 | instructions=( |
81 | 76 | "You are a helpful assistant that can take in all of the suggestions and advice from the other agents " |
82 | 77 | "and provide a detailed final travel plan. You must ensure that the final plan is integrated and complete. " |
|
87 | 82 | description="A helpful assistant that can summarize the travel plan.", |
88 | 83 | ) |
89 | 84 |
|
90 | | -# Create a manager agent for orchestration |
91 | | -manager_agent = ChatAgent( |
92 | | - chat_client=client, |
93 | | - instructions="You coordinate a team to complete travel planning tasks efficiently.", |
94 | | - name="magentic_manager", |
95 | | - description="Orchestrator that coordinates the travel planning workflow", |
| 85 | +manager_agent = Agent( |
| 86 | + client=client, |
| 87 | + description="Orchestrator that coordinates the research and coding workflow", |
| 88 | + instructions="You coordinate a team to complete complex tasks efficiently.", |
| 89 | + name="manager_agent", |
96 | 90 | ) |
97 | 91 |
|
98 | | -# Build the Magentic workflow |
99 | | -magentic_orchestrator = ( |
100 | | - MagenticBuilder() |
101 | | - .participants([local_agent, language_agent, travel_summary_agent]) |
102 | | - .with_manager( |
103 | | - agent=manager_agent, |
| 92 | +magentic_orchestrator = MagenticBuilder( |
| 93 | + participants=[local_agent, language_agent, travel_summary_agent], |
| 94 | + manager_agent=manager_agent, |
104 | 95 | max_round_count=20, |
105 | 96 | max_stall_count=3, |
106 | 97 | max_reset_count=2, |
107 | | - ) |
108 | | - .build() |
109 | | -) |
110 | | - |
111 | | - |
112 | | -async def main(): |
113 | | - # Keep track of the last message to format output nicely in streaming mode |
114 | | - last_message_id: str | None = None |
115 | | - output_event: WorkflowOutputEvent | None = None |
116 | | - |
117 | | - async for event in magentic_orchestrator.run_stream("Plan a half-day trip to Costa Rica"): |
118 | | - if isinstance(event, AgentRunUpdateEvent): |
119 | | - message_id = event.data.message_id |
120 | | - if message_id != last_message_id: |
121 | | - if last_message_id is not None: |
122 | | - console.print() # Add spacing after previous message |
123 | | - console.print(Rule(f"🤖 {event.executor_id}", style="bold blue")) |
124 | | - last_message_id = message_id |
125 | | - console.print(event.data, end="") |
126 | | - |
127 | | - elif isinstance(event, MagenticOrchestratorEvent): |
128 | | - console.print() # Ensure panel starts on a new line |
129 | | - if isinstance(event.data, ChatMessage): |
130 | | - # Show the plan creation in a panel |
131 | | - console.print( |
132 | | - Panel( |
133 | | - Markdown(event.data.text), |
134 | | - title=f"📋 Orchestrator: {event.event_type.name}", |
135 | | - border_style="bold green", |
136 | | - padding=(1, 2), |
137 | | - ) |
| 98 | +).build() |
| 99 | + |
| 100 | + |
| 101 | +def handle_event(event: WorkflowEvent, last_message_id: str | None) -> str | None: |
| 102 | + """Handle streaming events and return updated last_message_id.""" |
| 103 | + if event.type == "output" and isinstance(event.data, AgentResponseUpdate): |
| 104 | + message_id = event.data.message_id |
| 105 | + if message_id != last_message_id: |
| 106 | + if last_message_id is not None: |
| 107 | + console.print() |
| 108 | + console.print(f"🤖 {event.executor_id}:", end=" ") |
| 109 | + last_message_id = message_id |
| 110 | + console.print(event.data, end="") |
| 111 | + return last_message_id |
| 112 | + |
| 113 | + elif event.type == "magentic_orchestrator": |
| 114 | + console.print() |
| 115 | + emoji = "✅" if event.data.event_type.name == "PROGRESS_LEDGER_UPDATED" else "🦠" |
| 116 | + if isinstance(event.data.content, MagenticProgressLedger): |
| 117 | + console.print( |
| 118 | + Panel( |
| 119 | + json.dumps(event.data.content.to_dict(), indent=2), |
| 120 | + title=f"{emoji} Orchestrator: {event.data.event_type.name}", |
| 121 | + border_style="bold yellow", |
| 122 | + padding=(1, 2), |
138 | 123 | ) |
139 | | - elif isinstance(event.data, MagenticProgressLedger): |
140 | | - # Show a compact progress summary in a panel |
141 | | - ledger = event.data |
142 | | - satisfied = "✅" if ledger.is_request_satisfied.answer else "⏳ Steps pending" |
143 | | - progress = "✅" if ledger.is_progress_being_made.answer else "❌ Progress stalled" |
144 | | - loop = "⚠️ Loop detected" if ledger.is_in_loop.answer else "" |
145 | | - next_agent = ledger.next_speaker.answer |
146 | | - instruction = ledger.instruction_or_question.answer |
147 | | - |
148 | | - status_text = f"Plan satisfied? {satisfied} | Making progress? {progress} {loop}\n\n➡️ Next step: [bold]{next_agent}[/bold]\n{instruction}" |
149 | | - console.print( |
150 | | - Panel( |
151 | | - status_text, |
152 | | - title=f"📊 Orchestrator: {event.event_type.name}", |
153 | | - border_style="bold yellow", |
154 | | - padding=(1, 2), |
155 | | - ) |
| 124 | + ) |
| 125 | + elif hasattr(event.data.content, "text"): |
| 126 | + console.print( |
| 127 | + Panel( |
| 128 | + Markdown(event.data.content.text), |
| 129 | + title=f"{emoji} Orchestrator: {event.data.event_type.name}", |
| 130 | + border_style="bold green", |
| 131 | + padding=(1, 2), |
156 | 132 | ) |
157 | | - |
158 | | - |
159 | | - elif isinstance(event, WorkflowOutputEvent): |
160 | | - output_event = event |
161 | | - |
162 | | - if output_event: |
163 | | - console.print() # Add spacing |
164 | | - # The output of the Magentic workflow is a list of ChatMessages with only one final message |
165 | | - output_messages = cast(list[ChatMessage], output_event.data) |
166 | | - if output_messages: |
| 133 | + ) |
| 134 | + else: |
167 | 135 | console.print( |
168 | 136 | Panel( |
169 | | - Markdown(output_messages[-1].text), |
170 | | - title="🌎 Final Travel Plan", |
| 137 | + Markdown(str(event.data.content)), |
| 138 | + title=f"{emoji} Orchestrator: {event.data.event_type.name}", |
171 | 139 | border_style="bold green", |
172 | 140 | padding=(1, 2), |
173 | 141 | ) |
174 | 142 | ) |
175 | 143 |
|
| 144 | + return last_message_id |
| 145 | + |
| 146 | + |
| 147 | +def print_final_result(output_event: WorkflowEvent | None) -> None: |
| 148 | + """Print the final travel plan.""" |
| 149 | + if output_event: |
| 150 | + output_messages = cast(list[Message], output_event.data) |
| 151 | + console.print( |
| 152 | + Panel( |
| 153 | + Markdown(output_messages[-1].text), |
| 154 | + title="🌎 Final Travel Plan", |
| 155 | + border_style="bold green", |
| 156 | + padding=(1, 2), |
| 157 | + ) |
| 158 | + ) |
| 159 | + |
| 160 | + |
| 161 | +async def main(): |
| 162 | + last_message_id: str | None = None |
| 163 | + output_event: WorkflowEvent | None = None |
| 164 | + |
| 165 | + async for event in magentic_orchestrator.run("Plan a half-day trip to Costa Rica", stream=True): |
| 166 | + last_message_id = handle_event(event, last_message_id) |
| 167 | + if event.type == "output" and not isinstance(event.data, AgentResponseUpdate): |
| 168 | + output_event = event |
| 169 | + |
| 170 | + print_final_result(output_event) |
| 171 | + |
176 | 172 | if async_credential: |
177 | 173 | await async_credential.close() |
178 | 174 |
|
|
0 commit comments