Multi-Agent Workflow
Exercise 5 β Build a Multi-Agent Workflow
| Phase | Difficulty | Time Estimate |
|---|---|---|
| 3 β Orchestration | βββ Advanced | 30β40 min |
Learning Objectives
By the end of this exercise you will be able to:
- Create multiple specialist agents, each with different tools
- Connect agents into a pipeline using
WorkflowBuilderedges - Consume streaming events from the unified
WorkflowEvent(useevent.typeto discriminate:"data","executor_completed","output") to monitor workflow progress - Understand event-driven orchestration β why ordering, state, and business-process control matter
Prerequisites
| Requirement | How to verify |
|---|---|
| Exercises 1β4 completed | You are comfortable creating agents with tools and structured output |
| Azure CLI logged in | az account show (should return your subscription) |
.env configured |
FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_MODEL, and Bing connection vars set |
npx available |
npx --version (required for the MCP sequential-thinking tool) |
| Bing connection configured | BING_CONNECTION_ID (or custom Bing vars) set in .env |
Background
Why Workflows?
In Exercises 1β4 you ran a single agent at a time. Real-world scenarios often require multiple specialists that hand off work in a defined order β this is a workflow.
Workflows give you:
- Business-process control β enforce the order in which agents run
- State passing β each agent receives the output of the previous one
- Observability β streaming events let you track progress in real time
- Reliability β the framework manages agent lifecycle and error propagation
The WorkflowBuilder API
The framework provides WorkflowBuilder to assemble a directed graph of agents:
WorkflowBuilder() # create a builder
# In Agent Framework 1.2.2, pass start_executor=a (and output_executors=[...]) to WorkflowBuilder()
.add_edge(a, b) # a β b (b runs after a completes)
.add_edge(b, c) # b β c
.build() # returns a runnable Workflow
Call workflow.run_stream(prompt) to execute. It yields events as each agent progresses:
| Event Type | Purpose |
|---|---|
event.type == "data" |
An executor (agent) is actively producing intermediate updates β event.executor_id identifies which one |
event.type == "executor_completed" |
An executor finished β event.executor_id + event.data contain its result |
event.type == "output" |
An executor yielded a final output β event.data is the payload |
The Event Planning Scenario
In this exercise you will build a 5-agent pipeline that plans a corporate event:
Coordinator β Venue β Catering β Budget Analyst β Booking
Each agent specialises in one aspect and passes its work to the next:
| Agent | Role | Tool |
|---|---|---|
| coordinator | Creates a step-by-step plan for the team | MCPStdioTool (sequential-thinking) |
| venue | Recommends venues based on requirements | client.get_web_search_tool (Bing) |
| catering | Proposes food & beverage options | client.get_web_search_tool (Bing) |
| budget_analyst | Estimates costs and allocates budget | client.get_code_interpreter_tool |
| booking | Synthesises all outputs into a final event plan | (no tools β text synthesis only) |
Your Task
Open starter.py in this directory. You will see 8 TODO markers inside async def main(). The infrastructure code (imports, env loading, agent factory, helpers) is provided β your job is to wire up the agents, build the workflow, and handle the streaming events.
Step 1 β Create the Coordinator Agent (TODO 1)
Create an agent named "coordinator" with MCPStdioTool for sequential thinking:
- name:
"coordinator" - instructions: Describe the agent as an event coordinator who creates step-by-step plans
- tools:
MCPStdioTool(name="sequential-thinking", command="npx", load_prompts=False, args=["-y", "@modelcontextprotocol/server-sequential-thinking"])
Step 2 β Create the Venue Agent (TODO 2)
Create an agent named "venue" with client.get_web_search_tool:
- name:
"venue" - instructions: Describe the agent as a venue specialist
- tools:
client.get_web_search_tool(description="Search the web for current information using Bing", tool_properties=bing_props)
Step 3 β Create the Catering Agent (TODO 3)
Create an agent named "catering" with client.get_web_search_tool:
- name:
"catering" - instructions: Describe the agent as a catering coordinator
- tools: Same as venue (Bing search with
bing_props)
Step 4 β Create the Budget Analyst Agent (TODO 4)
Create an agent named "budget_analyst" with client.get_code_interpreter_tool:
- name:
"budget_analyst" - instructions: Describe the agent as a budget analyst who uses code for calculations
- tools:
client.get_code_interpreter_tool(description="Execute Python code for calculations: budget breakdowns, totals, per-person estimates, and simple what-if analysis.")
Step 5 β Create the Booking Agent (TODO 5)
Create an agent named "booking" with no tools β it synthesises all prior outputs:
- name:
"booking" - instructions: Describe the agent as an event booking specialist who creates a cohesive final plan
Step 6 β Build the Workflow (TODO 6)
Use WorkflowBuilder to chain the five agents:
coordinator β venue β catering β budget_analyst β booking
In Agent Framework 1.2.2, pass start_executor= and output_executors=[...] to WorkflowBuilder(...) constructor, then chain .add_edge() for each connection, then .build().
Step 7 β Run and Handle Streaming Events (TODO 7)
Call workflow.run_stream(prompt) and iterate over events with async for:
event.type == "data": Print which executor is active (avoid spamming β only print whenexecutor_idchanges)event.type == "executor_completed": Saveevent.datainto thecompleteddict keyed byevent.executor_idevent.type == "output": Saveevent.dataas thefinal_output
Step 8 β Print Results (TODO 8)
Iterate over the chain list and print each completed executorβs result using _print_result_item(). Fall back to final_output if no per-executor completions were captured.
Hints
Work through the hints progressively β try on your own first!
π‘ Hint 1 β Creating agents
Each agent is created with `await agent(name=..., instructions=..., tools=[...])`. The `agent` factory was returned by `_create_agent_factory()`. Agents without tools simply omit the `tools` parameter: ```python coordinator = await agent( name="coordinator", instructions="You are the Event Coordinator. ...", tools=[MCPStdioTool(...)], ) ```π‘ Hint 2 β Building the workflow
Chain edges to form a linear pipeline. The builder supports fluent chaining: ```python workflow = ( WorkflowBuilder() # NOTE: 1.2.2 requires start_executor= in WorkflowBuilder() constructor (see solution) .add_edge(coordinator, venue) .add_edge(venue, catering) .add_edge(catering, budget_analyst) .add_edge(budget_analyst, booking) .build() ) ```π‘ Hint 3 β Handling streaming events
Use `isinstance()` to check event types. Track the last executor ID to avoid printing duplicates: ```python events = workflow.run_stream(prompt) last_executor_id = None async for event in events: if event.type == "data": if event.executor_id != last_executor_id: print(f"-> {event.executor_id}") last_executor_id = event.executor_id elif event.type == "executor_completed": if event.data is not None: completed[event.executor_id] = event.data elif event.type == "output": final_output = event.data ```π‘ Hint 4 β Printing results
Iterate over the expected chain order and print each completed executor's result: ```python printed_any = False for executor_id in chain: if executor_id not in completed: continue printed_any = True print(f"### {executor_id}") _print_result_item(completed[executor_id]) print() if not printed_any and final_output is not None: _print_result_item(final_output) ```Validate Your Work
1. Run the check script (offline β no Azure needed)
bash workshop/exercises/ex5_workflow/check.sh
This verifies syntax, required code patterns (WorkflowBuilder, edges, event handling), and that all TODOs are resolved.
2. Run against Azure
python3 -u workshop/exercises/ex5_workflow/starter.py
Expected behaviour:
- The script connects to your Foundry project and creates 5 agents.
- You see executor progress lines like
-> coordinator,-> venue, etc. - Each agent runs in sequence β the full workflow takes 2β5 minutes depending on model speed.
- After completion, you see per-executor results (venue recommendations, catering plans, budget breakdown, final event plan).
- If OpenTelemetry is available,
[AGENT]and[TOOL]span lines appear.
Note: This exercise takes significantly longer than previous ones because 5 agents run sequentially. Be patient β the streaming events let you see progress in real time.
Bonus Challenges
- Add a 6th agent β e.g., a βtransportationβ specialist that plans logistics between the venue and attendee locations. Insert it between
venueandcateringin the edge chain. - Change the edge order β What happens if you swap
venueandcatering? Does the output quality change? - Add error handling per executor β Wrap the event loop in more granular error handling. What if one agent fails β can you still show partial results?
- Experiment with
max_iterationsβ Passmax_iterations=10toWorkflowBuilder()and observe how it affects workflow behaviour.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| Workflow takes > 5 minutes | Normal for 5 sequential agents with tools | Be patient β streaming events show progress. Consider reducing agent instructions if testing |
npx not found |
Node.js not installed | Install Node.js or run in the dev container where it is preinstalled |
| Bing connection error | BING_CONNECTION_ID not set or invalid |
Create a βGrounding with Bing Searchβ connection in the Foundry portal and set the ID in .env |
403 Forbidden |
RBAC permissions missing | Ensure your Entra ID has the appropriate role for the Foundry project. Run az login to refresh |
Failed to resolve model info |
Wrong model deployment name | Check FOUNDRY_MODEL matches a deployment in your Foundry projectβs βModels + endpointsβ |
No events from run_stream |
Possible SDK/endpoint mismatch | Verify agent-framework version matches requirements.txt. Check that the project endpoint is reachable |
RuntimeError: Required command ... |
npx not on PATH |
Install Node.js or ensure the dev container has it |
Solution Reference
See the complete working solution at: src/demo5_workflow_edges.py