Step 5

Multi-Agent Workflow

On this page

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:

  1. Create multiple specialist agents, each with different tools
  2. Connect agents into a pipeline using WorkflowBuilder edges
  3. Consume streaming events from the unified WorkflowEvent (use event.type to discriminate: "data", "executor_completed", "output") to monitor workflow progress
  4. 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 when executor_id changes)
  • event.type == "executor_completed": Save event.data into the completed dict keyed by event.executor_id
  • event.type == "output": Save event.data as the final_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

  1. Add a 6th agent β€” e.g., a β€œtransportation” specialist that plans logistics between the venue and attendee locations. Insert it between venue and catering in the edge chain.
  2. Change the edge order β€” What happens if you swap venue and catering? Does the output quality change?
  3. 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?
  4. Experiment with max_iterations β€” Pass max_iterations=10 to WorkflowBuilder() 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

← β†’ to navigate between steps